Cruyun's Blog


Talk is cheap, show you my code


木犀博客 前端开发过程

前言:木犀博客是我的第二个团队项目,这篇博客记录一些从搭建环境到最终完成的过程中我遇到的问题及其解决方案、各种发现与总结(我的进度簿里面也会记录),持续更新。


环境

这次的项目选择Vue框架并使用Webpack。开发环境是ninja2,搭建的步骤如下:

前续准备: npm node.js,我的分别是4.1.2 、v7.5.0

大概套路:
clone ninja下来

clone/新建 项目仓库

在项目里

1
2
npm install -g ninja_cli
touch ninja.conf.js

在ninja里面

1
npm link

在项目里

1
2
ninja2 init my-project webpack-vue
ninja2 start

要把新生成的文件拿出来。
可能遇到的错:

  • Uncaught TypeError: $export is not a function,解决:exclude: /node_modules/ after loader: ‘babel-loader’
  • 2-3个loader没装,手动install –save就好

还有就是刚开始我跑的还是ninja的旧版本,这次要用ninja2。ninja和ninja2的架子和配置有很多不同的地方。


PC端

首页

简书是个好图床.png

这个页面我分为以下几个组件:

  • 导航栏navigation(根据实际需求改成了HOME WEB DESIGN ANDROID PRODUCT ABOUT)
  • 博客页面content
  • 右上角的登录注册sign
  • 文章标签tags
  • 文章归档archive

遇到的已解决的问题有:

  1. 引入了一个组件可是整个页面是空白的,debug很久其实是<style lang='scss' module>没写对(被自己蠢哭)还有就是用了<navigation>做组件标签名字,和HTML原本存在的标签名字冲突了
  2. 各类别的父组件里面给后台发送请求,用props传数据给content组件,这样就可以根据类别拉取不同数据

  3. content: 在父组件,向后台发送请求拿到博客的所有参数,并且传给子组件。翻页的话,子组件里面要保存总页数和当前页数,点击向上或者向下翻页,当前页数改变传递给父组件,父组件收到之后重新拉取数据。【当前页数】是父子组件的双向传递,子->父 使用event emitter 子组件触发事件,父组件监听。

  4. web/android..等页面渲染不出来,因为template里面include svg.html,svg的id也命名为web/android..就冲突了

  5. 用a标签,:href=""跳转到second页面,那second页面要fetch数据,怎么拿到id呢: 从URL里面拿,window.location拿到URL,用split('/')处理字符串获得id

  6. hover博客的box是出现半透明的蒙版和“阅读全文”的字:使用伪元素:before或者:after,控制hover的时候伪元素的样式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    .content_box {
    width: 100%;
    margin-bottom: 30px;
    font-size: 0;
    background-color: $white;
    }
    .mask {
    position: relative;
    width: 100%;
    height: 210px;
    }
    .mask:before {
    content: '阅读全文';
    text-align: center;
    line-height: 210px;
    display: block;
    width: 600px;
    position: absolute;
    top: 0;
    }
    .mask:hover::before {
    background-color:rgba(82, 139, 139, 0.7);
    font-size: 24px;
    color: $white;
    }
  7. 返回对应分类的博客不能用一个单页多个router去写,抽出来一个组件放在多个页面,根据类别拉取不同的数据

  • Point 1 Using Mixins in Vue.js

    • Usage:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      import Child from './Child'
      import { toggle } from './mixins/toggle'
      export default {
      name: 'modal',
      mixins: [toggle],
      components: {
      appChild: Child
      }
      }
      • my example:home.js
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      import Vue from 'vue'
      import Sort from './components/Sort'
      import style from './scss/reset.scss'
      const App = {
      template: "#app",
      mixins: [Sort]
      }
      App.el = "#app"
      const app = new Vue(App)

      Sort.vue是home、web、design…页面公用的。本来只写了一个js,把所有的vue实例都放在了这里,但是这样进入到其中一个页面的时候,会请求七次同样的数据。于是分开写了各个页面的JS文件。

      由于各个页面里面的组件是重复的,在webpack里面需要做个处理,增加一个chunk把各个页面重复的组件放在一起

      1
      2
      3
      4
      new webpack.optimize.CommonsChunkPlugin({
      name: 'common',
      chunks: ['sort','web','android','design','product','tagBlogs','archiveBlogs']
      }),

      然后在各自的chunk里面加上common

      1
      2
      3
      4
      5
      6
      7
      new HtmlWebpackPlugin({
      alwaysWriteToDisk: true,
      filename: 'template/index.html',
      inject: false,
      template: path.join(__dirname, '../template/index.ejs'),
      chunks: ['common','home']
      }),

      注意一下chunks的顺序,先写common

      打包处理的结构是这样的
      common.js

二级页面:查看一篇博客文章

木犀博客二级

这个页面我分为以下几个部分:

  • 组件:导航栏navigation(根据实际需求改成了HOME WEB DESIGN ANDROID PRODUCT ABOUT)
  • 博客文章
  • 组件:右上角的登录注册sign
  • 组件:评论框CommentBox

主要点

  • 评论框组件:本来想评论成功之后直接将评论框的内容push到comments数组里面,但是还需要获取用户的ID和发布评论的时间,就重新fetch所有评论的数据。所以在评论框组件里面触发click事件提交新评论,this.$parent.fetchCommet()调用父组件的方法重新拉取评论列表
  • 发评论需要token

添加一篇博客文章

这个页面我分为以下几个部分:

  • 组件:导航栏navigation(根据实际需求改成了HOME WEB DESIGN ANDROID PRODUCT ABOUT)
  • 编辑博客文章
  • 组件:右上角的登录注册sign

主要点

  • 选择组别:每个input有个各自的value,再用v-model="group"保存下来
  • 添加tag:类似于一个TODO LIST,用v-for显示tag,input框里输入tag,按回车键触发事件用@key.enterup="addTag"。需要注意的是,这里不仅仅只有tagtags[],否则你删除tag的时候无论点击哪一个delete,都会作用在最新添加的tag。因为tag一直在更新,调用removeTag()的时候,传入的参数是最新的tag。所以需要另一个变量newTag,让添加进数组里面的tag为newTag,这样子tags[]里面的元素是tag,而调用removeTag()时传入的参数是newTag,这样子this.tags.indexOf(tag)就能找到对应的tag。

两个函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
addTag() {
var tag = this.newTag.trim();
if (tag && this.tags.length < 5) {
this.tags.push(tag);
this.newTag = "";
} else if (this.tags.length >= 5) {
this.tip = true
}
},
removeTag(tag) {
var index = this.tags.indexOf(tag);
this.tags.splice(index, 1);
}

编辑一篇博客文章

刚开始我想的太复杂了,像拿到文章,然后再编辑,其实只要 mounted 时拿到原来的博客,再按照发送那样写就可以了。于是我把原来的发送页面的部分拿出来作为编辑和发送页面的组件,只用传一个 flag 区别,通过flag 判断是否要去拿原来的博客,以及分别使用 method 和 API。


移动端

扎心了,又要适配各种手机和浏览器了

写移动端才发现要复用组件,于是抛弃了CSS Modules。<style lang="scss" module>把module删掉,template里面也不需要写:class="$style.xxx"。写pc.scss和mobile.scss,再按需引入。(vue loader自带了CSS Modules,无需自己install)

和PC端不同的是,移动端没有发博客编辑博客的功能,并且把标签和归档栏放到了导航栏。

首页

移动端首页

  1. header: 唤出导航栏的按钮在左边,logo居中,登录入口在右边。实现这个效果,我用的方法有点粗暴:父元素用flex布局,让子元素(logo)居中;左右两个按钮position: absolute设置topleftright调整位置。

  2. 抽屉式导航栏: 用了vue transition。

    • vue transition和CSS Modules一起用会出现bug,因为CSS Modules把class名称转成base64,动画找不到对应绑定的class。
    • 注意z-index的使用:父元素要position: relative;,子元素position: absolute;

————————— 2.5 更新 ——————–

希望改完首屏渲染的bug就能正式结束这个项目